package vget

import (
	"fmt"
//	"log"
	"net/url"
	"net/http"
	"io/ioutil"
    "strconv"
    "os"
    "path"
	"strings"
)


type downloadInfo struct {

    DownloadUrl    *url.URL
    Extension    string
    VideoSize    int64
    Width    int
    Height   int
    Length   int64

}

type VideoInfo struct {

    Title    string
    VideoUrl    *url.URL
    Dinfo    []downloadInfo
    Sel    int

}

func (vinfo VideoInfo)Download(path_str string) (full_path string, err error) {
    
    selected_dinfo := vinfo.Dinfo[vinfo.Sel]

    // create file
    file_name := vinfo.Title + "." + selected_dinfo.Extension
    full_path = path.Join( path_str, file_name )
    fd, err := os.Create(full_path)
    if err != nil { return }

    // get video to buffered reader (http reader)
    resp, err := http.Get( selected_dinfo.DownloadUrl.String() )
    if err != nil { return }

    // read all buffer to qs
    qs, err := ioutil.ReadAll(resp.Body)
    resp.Body.Close()
    if err != nil { return }

    // write qs to file
    fd.Write(qs)

    // close file
    fd.Close()

    return

}

type VGetError struct {
}

func (e *VGetError) Error() string {
    return "nothing~"
}


func GetVideoInfo(rawurl string, try_limit int) (vinfo VideoInfo, err error ) {

    u, err := url.Parse( strings.Replace( rawurl, "/www.", "/", -1 ) )
    if err != nil { return }

    if u.Host == "youtube.com" {
        vinfo, err = getYoutubeVideoInfo(*u, try_limit)
        vinfo.VideoUrl = u
        if err != nil { return }
    }else if u.Host == "vimeo.com" {
    }else{
        fmt.Printf("ERROR!! unknown hosts: %s\n", u.Host)
    }

    return vinfo, err

}


func getYoutubeVideoInfo(yt_url url.URL, try_limit int) (vinfo VideoInfo, err error) {

    // make request for video info
    yt_url.Path = "/get_video_info"
    q := yt_url.Query()
    video_id := q.Get("v")
    q.Del("v")
    q.Add("video_id", video_id)
    q.Add("el", "embedded")
    q.Add("ps", "default")
    q.Add("eurl", "")
    yt_url.RawQuery = q.Encode()

    // to make sure first loop works
    arr_size := 0
    sig_size := arr_size + 1
    tries := 0

    for arr_size != sig_size && tries < try_limit {

        // get http response
        resp, err2 := http.Get( yt_url.String() )
        if err2 != nil { err = err2; return }

        // make rune to UTF-8 string (query string)
        qs, err2 := ioutil.ReadAll(resp.Body)
        resp.Body.Close()
        if err2 != nil { err = err2; return }
    
        // make map from query string
        m1, err2 := url.ParseQuery( string( qs ) )
        if err2 != nil { err = err2; return }
    
        // get encoded part from query string
        url_encoded_fmt_stream_map := m1.Get("url_encoded_fmt_stream_map")
        if url_encoded_fmt_stream_map == "" { return }
    
        // make map from url encoded stream string
        m2, err2 := url.ParseQuery( url_encoded_fmt_stream_map )
        if err2 != nil { return }
    
        // make array downloadInfo field in vinfo
        vinfo.Title = strings.Replace( m1.Get("title"), " ", "", -1 )
    
        dur, err2 := strconv.ParseInt( m1.Get("length_seconds"), 0, 0 )
        if err2 != nil { err = err2; return }

        urls := m2["url"]
        sigs := m2["sig"]

        arr_size = len( urls )
        sig_size = len( sigs )

        vinfo.Dinfo = make( []downloadInfo, arr_size )

        // if not same, retry
        if ( arr_size != sig_size ){
            continue
        }

        // do final job. for each urls,
        for idx, val := range urls {
    
            // get unescaped string, qstr
            qstr, err3 := url.QueryUnescape( val )
            if err3 != nil { err = err3; return }
    
            // make url type from qstr, u
            u, err3 := url.Parse( qstr )
            if err3 != nil { err = err3; return }

            // get query from u, q
            q := u.Query()
            // add signature query to q
            q.Add( "signature", sigs[idx] )
            u.RawQuery = q.Encode()
            vinfo.Dinfo[idx].DownloadUrl = u
    
            itag, err3 := strconv.ParseInt( q.Get("itag"), 0, 0 )
            if err3 != nil { err = err2; return }
    
            vinfo.Dinfo[idx].Length = dur
            if err3 != nil { err = err2; return }
    
            vinfo.Dinfo[idx].Extension, vinfo.Dinfo[idx].Width, vinfo.Dinfo[idx].Height = setVideoQuality( int( itag ) )
        }
    }

    return

}

func setVideoQuality(itag int) (ext string, width int, height int){
/*    Switch)
    {
        case 5: q.SetQuality("flv", new Size(320, (_Wide ? 180 : 240))); 
        case 6: q.SetQuality("flv", new Size(480, (_Wide ? 270 : 360))); break;
        case 17: q.SetQuality("3gp", new Size(176, (_Wide ? 99 : 144))); break;
        case 18: q.SetQuality("mp4", new Size(640, (_Wide ? 360 : 480))); break;
        case 22: q.SetQuality("mp4", new Size(1280, (_Wide ? 720 : 960))); break;
        case 34: q.SetQuality("flv", new Size(640, (_Wide ? 360 : 480))); break;
        case 35: q.SetQuality("flv", new Size(854, (_Wide ? 480 : 640))); break;
        case 36: q.SetQuality("3gp", new Size(320, (_Wide ? 180 : 240))); break;
        case 37: q.SetQuality("mp4", new Size(1920, (_Wide ? 1080 : 1440))); break;
        case 38: q.SetQuality("mp4", new Size(2048, (_Wide ? 1152 : 1536))); break;
        case 43: q.SetQuality("webm", new Size(640, (_Wide ? 360 : 480))); break;
        case 44: q.SetQuality("webm", new Size(854, (_Wide ? 480 : 640))); break;
        case 45: q.SetQuality("webm", new Size(1280, (_Wide ? 720 : 960))); break;
        case 46: q.SetQuality("webm", new Size(1920, (_Wide ? 1080 : 1440))); break;
        case 82: q.SetQuality("3D.mp4", new Size(480, (_Wide ? 270 : 360))); break;        // 3D
        case 83: q.SetQuality("3D.mp4", new Size(640, (_Wide ? 360 : 480))); break;        // 3D
        case 84: q.SetQuality("3D.mp4", new Size(1280, (_Wide ? 720 : 960))); break;       // 3D
        case 85: q.SetQuality("3D.mp4", new Size(1920, (_Wide ? 1080 : 1440))); break;     // 3D
        case 100: q.SetQuality("3D.webm", new Size(640, (_Wide ? 360 : 480))); break;      // 3D
        case 101: q.SetQuality("3D.webm", new Size(640, (_Wide ? 360 : 480))); break;      // 3D
        case 102: q.SetQuality("3D.webm", new Size(1280, (_Wide ? 720 : 960))); break;     // 3D
        case 120: q.SetQuality("live.flv", new Size(1280, (_Wide ? 720 : 960))); break;    // Live-streaming - should be 
        default: q.SetQuality("itag-" + itag, new Size(0, 0)); break;       // unknown or parse 
    }
*/

switch itag {
        case 5: ext = "flv"; width = 320; height = 180
    case 6: ext = "flv"; width = 480; height = 270
case 17: ext = "3gp"; width = 176; height = 99
    case 18: ext = "mp4"; width = 640; height = 360
    case 22: ext = "mp4"; width = 1280; height = 720
    case 34: ext = "flv"; width = 640; height = 360
    case 35: ext = "flv"; width = 854; height = 480
    case 36: ext = "3gp"; width = 320; height = 180
    case 37: ext = "mp4"; width = 1920; height = 1080
    case 38: ext = "mp4"; width = 2048; height = 1152
    case 43: ext = "webm"; width = 640; height = 360
    case 44: ext = "webm"; width = 854; height = 480
    case 45: ext = "webm"; width = 1280; height = 720
    case 46: ext = "webm"; width = 1920; height = 1080
/*        case 82: ext = "3D.mp4";, new Size(480, (_Wide ? 270 : 360))); break;        // 3D
        case 83: ext = "3D.mp4";, new Size(640, (_Wide ? 360 : 480))); break;        // 3D
        case 84: ext = "3D.mp4";, new Size(1280, (_Wide ? 720 : 960))); break;       // 3D
        case 85: ext = "3D.mp4";, new Size(1920, (_Wide ? 1080 : 1440))); break;     // 3D
        case 100: ext = "3D.webm";, new Size(640, (_Wide ? 360 : 480))); break;      // 3D
        case 101: ext = "3D.webm"; new Size(640, (_Wide ? 360 : 480))); break;      // 3D
        case 102: ext = "3D.webm"; new Size(1280, (_Wide ? 720 : 960))); break;     // 3D
        case 120: ext = "live.flv"; new Size(1280, (_Wide ? 720 : 960))); break;    // Live-streaming - should be */
    default: ext = "itag-"; width = 0; height = 0       // unknown or parse 
    }

    return

}


func main() {
	
    vinfo, err := GetVideoInfo( os.Args[1], 10 )

    full_path, err := vinfo.Download( os.Args[2] )
    if err != nil { fmt.Println(err); return }

    fmt.Printf("\"%s\" is downloaded!\n", full_path)

    return

}
